﻿using InSafetyAirServer.Database;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data.Linq.Mapping;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
using System.Security.Cryptography;
using InSafetyAirServer.Model;
using System.Reflection;
using System.Data.Linq;

namespace InSafetyAirServer.Controller
{
    class Functions
    {
        private static Functions _instance = null;
        public SQLDatabaseDataContext _db = null;

        private Functions()
        {
            _db = new SQLDatabaseDataContext();
        }

        public static Functions getInstance()
        {
            if (_instance == null)
            {
                _instance = new Functions();
            }
            return _instance;
        }

        public T[] GetAll<T>() where T : class
        {
            var table = _db.GetTable<T>();
            return table.ToArray();
        }

        public T[] GetAllByField<T>(string field, object value) where T : class
        {
            var param = Expression.Parameter(typeof(T), "row");
            var lambda = Expression.Lambda<Func<T, bool>>(
                Expression.Equal(
                    Expression.PropertyOrField(param, field),
                    Expression.Constant(value, value.GetType())), param);

            var table = _db.GetTable<T>();
            return table.Where(lambda).ToArray();
        }

        public T GetById<T>(int id) where T : class
        {
            var param = Expression.Parameter(typeof(T), "row");
            var lambda = Expression.Lambda<Func<T, bool>>(
                Expression.Equal(
                    Expression.PropertyOrField(param, GetPrimaryKeyName<T>()),
                    Expression.Constant(id, id.GetType())), param);

            var table = _db.GetTable<T>();
            return table.Single(lambda);
        }

        public T AddToDatabase<T>(T obj) where T : class
        {
            var table = _db.GetTable<T>();
            table.InsertOnSubmit(obj);
            _db.SubmitChanges();
            return obj;
        }

        public void insOnSubmit<T>(T obj) where T : class
        {
            var table = _db.GetTable<T>();
            table.InsertOnSubmit(obj);
        }

       


        public void submit()
        {
            _db.SubmitChanges();
        }

        public void updatePerson(Person pers)
        {
            Person _dbPerson = GetById<Person>(pers.personId);
            _dbPerson.fName = pers.fName;
            _dbPerson.sName = pers.sName;
            _dbPerson.phone = pers.phone;
            _dbPerson.address = pers.address;
            _dbPerson.gender = pers.gender;
            _dbPerson.passportNo = pers.passportNo;
            _db.SubmitChanges();
        }

        public void updateFlight(Flight fligh)
        {
            Flight _dbFLight = GetById<Flight>(fligh.flightId);
            _dbFLight.flightNumber = fligh.flightNumber;
            _dbFLight.airLineId = fligh.airLineId;
            _dbFLight.planeId = fligh.planeId;
            _db.SubmitChanges();
        }

        public void updateSegemnt(FlightSegment seg)
        {
            FlightSegment _dbFS = GetById<FlightSegment>(seg.flightSegmentId);
            _dbFS.flightId = seg.flightId;
            _dbFS.startAirportId = seg.startAirportId;
            _dbFS.endAirportId = seg.endAirportId;
            _db.SubmitChanges();
        }

        public void updateSchedule(Schedule sch)
        {
            Schedule _dbPerson = GetById<Schedule>(sch.scheduleId);
            _dbPerson.startTime = sch.startTime;
            _dbPerson.endTime = sch.endTime;
            _dbPerson.price = sch.price;
            _db.SubmitChanges();
        }

        public void UpdateToDatabase<T>(T obj) where T : class
        {
            T dbObj = GetById<T>((int)obj.GetType().GetProperty(GetPrimaryKeyName<T>()).GetValue(obj, null));
            if (dbObj != null)
            {

                int i = 0;
                foreach (PropertyDescriptor item in TypeDescriptor.GetProperties(obj))
                {
                    if (i == 0)
                    {

                    }
                    else
                        item.SetValue(dbObj, item.GetValue(obj));
                    i++;
                }
            }
            _db.SubmitChanges();
        }

        public void DeleteFromDatabase<T>(T obj) where T : class
        {
            T dbObj = GetById<T>((int)obj.GetType().GetProperty(GetPrimaryKeyName<T>()).GetValue(obj, null));
            if (dbObj != null)
            {
                var table = _db.GetTable<T>();
                table.DeleteOnSubmit(dbObj);
            }
            _db.SubmitChanges();
        }

        public string GetPrimaryKeyName<T>() where T : class
        {
            MetaType meta = _db.Mapping.GetTable(typeof(T)).RowType;

            return meta.IdentityMembers[0].Member.Name;
        }

        public void CopyProperties(object dest, object src)
        {
            foreach (PropertyDescriptor item in TypeDescriptor.GetProperties(src))
            {

                item.SetValue(dest, item.GetValue(src));
            }
        }

        public string generateSalt()
        {
            var chars = "qwertyuiopasdfghjklzxcvbnmABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
            var random = new Random();
            var result = new string(
            Enumerable.Repeat(chars, 32)
                  .Select(s => s[random.Next(s.Length)])
                  .ToArray());

            return result;
        }

        public string CalculateMD5Hash(string input)
        {
            // step 1, calculate MD5 hash from input

            MD5 md5 = System.Security.Cryptography.MD5.Create();
            byte[] inputBytes = System.Text.Encoding.ASCII.GetBytes(input);
            byte[] hash = md5.ComputeHash(inputBytes);

            // step 2, convert byte array to hex string
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < hash.Length; i++)
            {
                sb.Append(hash[i].ToString("x2"));
            }
            return sb.ToString().Substring(0, 32);
        }

        public Account checkLogin(string username, string password)
        {
            Account account = new Account();
            try
            {
                account = _db.Accounts.Single(a => a.username.Equals(username));
            }
            catch (Exception)
            {
                return null;
            }

            //return "pasword:" + password + "|account salt: " + account.salt + "| acc password: " + account.password + " " + CalculateMD5Hash(password + account.salt);
            if ((CalculateMD5Hash(password + account.salt)).Equals(account.password))
                return account;
            return null;


        }

        public IList<Type> GetTypesInNamespace(Assembly assembly, string nameSpace)
        {
            return assembly.GetTypes().Where(t => String.Equals(t.Namespace, nameSpace, StringComparison.Ordinal)).ToList();
        }

        public IList<Type> GetDatabaseClasses()
        {
            return new List<Type> { typeof(Account), typeof(AirLine), typeof(Airport), typeof(BL_S), typeof(Booking), typeof(BookingLine), typeof(Flight), typeof(FlightSegment), typeof(Person), typeof(PersonBookingLine), typeof(Plane), typeof(PlaneModel), typeof(Schedule) };
        }

        public IEnumerator<Type> GetDatabaseClassesEnumerator()
        {
            return GetDatabaseClasses().GetEnumerator();
        }

        public Schedule findSchedule(FlightSegment flightSegment, DateTime startTime, DateTime endTime)
        {
            IEnumerator<Schedule> enumerator = flightSegment.Schedules.GetEnumerator();
            bool find = false;
            Schedule theSchedule = null;
            while (enumerator.MoveNext() && !find)
            {
                Schedule schedule = enumerator.Current;
                if (schedule.startTime == startTime && schedule.endTime == endTime)
                {
                    find = true;
                    theSchedule = schedule;
                }

            }
            return theSchedule;

        }

        public int findFreeSeats(Schedule schedule)
        {
            int totalSeats = schedule.FlightSegment.Flight.Plane.PlaneModel.seats;
            int ocupiedSeats = 0;
            foreach (BL_S bl_s in GetAllByField<BL_S>("scheduleId", schedule.scheduleId))
            {
                ocupiedSeats += GetAllByField<PersonBookingLine>("bookingLineId", bl_s.bookingLineId).Length;
            }

            return totalSeats - ocupiedSeats;
        }

    }

}
